package org.codert.jpa.interceptor;

import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * @see org.codert.jpa.config.MyMvcConfiguration 在此进行了注册
 * @description: SpringMvc拦截器
 * @author: TanJingyu
 * @create: 2021-09-23 22:59
 **/
public class MyInterceptor implements HandlerInterceptor {

    /**
     * 这是做了个定制化response的操作，但是有个限制
     * 如果controller返回的是页面地址，那么在以下三个方法中都可以修改
     * 但是若是返回的@responseBody或者直接返回的ResponseEntity，那么只有PreHandle有效
     * 其他两个还没执行, response就被提交了
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("拦截器....preHandle");

        CookieUtil.addCookie(response, "name", "王吉");

        // 有可能不是HandlerMethod
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod)handler;

            // 这就是jdk反射包中的Method
            Method method = handlerMethod.getMethod();

            // 这是SpringMvc定义的类型，里边包装了jdk反射的Parameter
            MethodParameter[] methodParameters = handlerMethod.getMethodParameters();

            if (methodParameters.length > 0) {
                // 这是SpringMvc的方法，可以获取当前方法的泛型参数类型
                // GenericParameterType意为泛型参数类型，generic单词有泛型的意思
                // Type是jdk反射的类，其下实现有Class，GenericParameterType
                // 当方法的入参为泛型参数时，我们想查看泛型真正的类型，那么可以调用GenericParameterType中的方法
                // java的泛型是擦除机制的，但是不代表运行时什么线索都没留给我们，java依然在GenericParameterType这个Type中给我们留了线索
                // 例如方法 void show(List<String> strings); 那么通过GenericParameterType可以得到String类型
                Type genericParameterType = methodParameters[0].getGenericParameterType();
                Class<?> parameterType = methodParameters[0].getParameterType();


                // 说在后面：不是只能在SpringMvc定义的这个MethodParameter才能获取到Type，其实它也是从Jdk的反射类中获取的
                // 我们亦可以从比如Method中获取，例如
                Type[] genericParameterTypes = method.getGenericParameterTypes();
                for (Type type : genericParameterTypes) {
                    if (type instanceof ParameterizedType) {
                        System.out.println(type.getClass().getName());
                    }
                }

                // 比如说SpringBoot源码中的ObjectProvider就是用的这种方式实现泛型注入的
                // @see org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
            }
        }

        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("拦截器....postHandle");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("拦截器....afterCompletion");
    }


    public static class CookieUtil {
        public static void addCookie(HttpServletResponse response, String key, String value) {
            Cookie cookie = new Cookie(key, value);
            cookie.setPath("/");
//            cookie.setDomain();
//            cookie.setHttpOnly();
//            cookie.setMaxAge();
            cookie.setVersion(8);

            response.addCookie(cookie);
        }
    }
}
